#include <myfilelog.hxx>
#include <loghelper.hxx>
#include <tcpserversocket.hxx>
#include <socketexception.hxx>
#include <was_api.h>
#include <utility.hxx>
#include <macro.hxx>

#include "priv/auconnmanager.hxx"
#include "priv/atkconnmanager.hxx"
#include "priv/auconn.hxx"

    AuConnManager::AuConnManager(tUInt32 auport, AtkConnManager* atkconn_manager,FileLog *filelog)
: Thread(),
    _auconn_vec(),
    _auconn_vec_mutex(),
    _auport(auport),
    _atkconn_manager(atkconn_manager),
    _proxies(),
    _proxies_mutex(),
    _is_shutdown_requested(false),
    _filelog(filelog)
{
}

AuConnManager::~AuConnManager()
{
}

void AuConnManager::Shutdown(void)
{
    _is_shutdown_requested=true;
}

void AuConnManager::SendToAttackers(const tBridgeMsg* bmsg)
{
    _atkconn_manager->Send(bmsg);
}

void AuConnManager::SendToAutoers(const tBridgeMsg* bmsg, const AuConn* except)
{
    Utility::Lock lock(&_auconn_vec_mutex);
    tAuConnVec::iterator iter = _auconn_vec.begin();
    while (iter != _auconn_vec.end())
    {
        if ((*iter) != except)
        {
            tBridgeMsg* dup = DupBMsg(bmsg);
            (*iter)->SendMsg(dup);
        }

        ++iter;
    }
}
void AuConnManager::Execute(Thread::Arg /*arg*/)
{
    TcpServerSocket *listening_socket;
    TcpSocket *new_socket;
    AuConn *new_conn;
    tUInt32 counter=0;

    try
    {
        listening_socket = new TcpServerSocket(_auport);

    }catch(SocketException &e)
    {
        LOGERROR("Exception happened while instantiating listening socket in AuConnManager:\n"<<e.Description()<<endl);
        return;
    }

    APPENDMSGEX("AuConnManager started.", 3);
    while(!_is_shutdown_requested)
    {
        //For it's time consuming, we do it once every 250 loops.
        counter++;
        if (counter==250)
        {   
            counter=0;
            ClearConns();
        }

        // Check if there is a new connection request
        // Wait and check within 100 milliseconds
        if (0 >= listening_socket->IsReadyForRead(100))
        {
            Yield();
            continue;
        }


        // Instanciating new socket for incoming connection.
        try 
        {
            new_socket=listening_socket->Accept();
        }
        catch(SocketException &e)
        {
            LOGERROR("AuConnManager: Failed to instanciate socket for new incoming connection: "<<e.Description()<<".");
            continue;
        }

        APPENDMSGEX("AuConnManager: Received a new connection from "<<new_socket->PeerHostAddress()<<":"<<new_socket->PeerHostPort(), 3);

        new_socket->SetKeepAlive();

        new_conn=new AuConn(this, new_socket, _filelog);
		new_conn->Start();

        Utility::Lock lock(&_auconn_vec_mutex);
        _auconn_vec.push_back(new_conn);
    } // while(!_is_shutdown_requested)

    APPENDMSGEX("AuConnManager shutting down...", 3);

    // First making sure no more incoming connection is accepted.
    listening_socket->Disconnect();
    delete listening_socket;
    listening_socket=0;

    ClearConns(true);

    APPENDMSGEX("AuConnManager shutdowned", 3);
}

AuConnManager::AuConnManager(const AuConnManager &model)
: Thread(),
    _auconn_vec(),
    _auport(model._auport),
    _is_shutdown_requested(model._is_shutdown_requested),
    _filelog(model._filelog)
{
}
void AuConnManager::ClearConns(bool last)
{
    Utility::Lock lock(&_auconn_vec_mutex);

    int cleared = 0;
    tAuConnVec::iterator iter = _auconn_vec.begin();
    while(iter != _auconn_vec.end())
    {
        AuConn* auconn = *iter;

        if (last)
        {
            auconn->Shutdown();
            auconn->Join();
        }

        if ((*iter)->State() == Thread::FINISHED_STATE)
        {
           auconn->Join();
           delete auconn;
           auconn = 0;

           iter = _auconn_vec.erase(iter);

           cleared++;
        }
        else
        {
            ++iter;
            Yield();
        }
    }

    if (cleared)
    {
        APPENDMSGEX("Cleared "<<cleared<<" AuConn(s), remain "<<_auconn_vec.size(), 3);
    }
}
